/* ************************************************************************ */
/*                                                                          */
/*  Copyright (c)2005 Russell Weir | damonsbane _at_ gmail                  */
/*                                                                          */
/* This library is free software; you can redistribute it and/or            */
/* modify it under the terms of the GNU Lesser General Public               */
/* License as published by the Free Software Foundation; either             */
/* version 3.0 of the License, or (at your option) any later version.       */
/*                                                                          */
/* This library is distributed in the hope that it will be useful,          */
/* but WITHOUT ANY WARRANTY; without even the implied warranty of           */
/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU        */
/* Lesser General Public License or the LICENSE file for more details.      */
/*                                                                          */
/* ************************************************************************ */

package tz;
import altdate.Gregorian;
import altdate.JulianDay;
import altdate.AltDate.TimeStruct;

class Zone {
	public var name(default,null)	: String;

	public function new(s:String) { name = s; }

	public function getAbbrev() : Array<String> {
		throw "getAbbrev() not implemented for " + name;
		return null;
	}

	public function getChanges() : Array<ChangeRecord> {
		throw "getChanges() not implemented for " + name;
		return null;
	}

	/**
		Calculate the JDN a Gregorian date or date/time, that is assumed
		to be a local date and time.
		Normally, you don't use this method, it is called
		from Gregorian.set()
	*/
	// Note: Code is very similar, and should be in sync with,
	// altdate.JulianDay.fromGregorian()
	// The difference is, this version modifies the Gregorian
	// object with proper offset, save, abbreviation and value
	public function localToUtc(gdt : Gregorian) : Void
	{
		//trace(here.methodName + " " + name);
		var origAsTime = gdt.isDateTime();

#if DEBUG_BUILD
		var seconds : Int = (gdt.hours * 3600) +
				(gdt.minutes * 60) +
				gdt.seconds;
		if(!origAsTime) {
			if(seconds != 0) {
				throw "Trying to convert Gregorian 'dateonly' with "+Std.string(seconds)+" seconds in it.";
			}
		}
#else
		var seconds : Int = 0;
		if(origAsTime) {
			seconds = (gdt.hours * 3600) +
				(gdt.minutes * 60) +
				gdt.seconds;
		}
#end
		// get the julian value for the m/d/y h:m:s in the Gregorian date
		// this is a pretend UTC value for the time being
		var val : Float = altdate.JulianDay.calculate(gdt.year,gdt.month,gdt.day, seconds);

		var record : ChangeRecord = null;
		var _changes : Array<ChangeRecord> = getChanges();
		var _abbrev : Array<String> = getAbbrev();

		for(i in _changes) {
			if(i.jdn <= val)
				record = i;
			if(i.jdn > val)
				break;
		}
		if(record == null) {
			throw "Could not find a record to convert this date";
		}

		var ts : TimeStruct = JulianDay.convertToTimeStruct(val);
		Reflect.setField(gdt,"year",ts.year);
		Reflect.setField(gdt,"month",ts.month);
		Reflect.setField(gdt,"day",ts.day);
		if(origAsTime) {
			Reflect.setField(gdt,"hours",ts.hours);
			Reflect.setField(gdt,"minutes",ts.minutes);
			Reflect.setField(gdt,"seconds",ts.seconds);
		}
		else {
			Reflect.setField(gdt,"hours",0);
			Reflect.setField(gdt,"minutes",0);
			Reflect.setField(gdt,"seconds",0);
		}
		//trace(ts);
		//trace(record.utco +" "+ record.s);
		//trace(val);
		// adjust the value by the UTC offset plus the DST save value
		val = val - ((record.utco + record.s)/86400);
		//trace(val);

		Reflect.setField(gdt,"value",val);
		Reflect.setField(gdt,"offset",record.utco);
		Reflect.setField(gdt,"save",record.s);
		Reflect.setField(gdt,"tzabbrev",_abbrev[record.i]);
	}

	/**
		Apply Julian Day number to Gregorian, using the
		timezone info in the Gregorian object. Sets
		the y/m/d h:m:s. If the Gregorian is a date only,
		the time will be reset to 0:0:0 even after this
		conversion.
		Normally, you don't use this method, it is called
		from Gregorian.setValue()
	*/
	// Note: Code is very similar, and should be in sync with,
	// altdate.JulianDay.fromGregorian()
	// The difference is, this version modifies the Gregorian
	// object with proper offset, save, abbreviation and value
	public function applyJdn(gdt : Gregorian, val : Float) : Void
	{
		var origAsTime = gdt.isDateTime();

		var record : ChangeRecord = null;
		var _changes : Array<ChangeRecord> = getChanges();
		var _abbrev : Array<String> = getAbbrev();

		for(i in _changes) {
			if(i.jdn <= val)
				record = i;
			if(i.jdn > val)
				break;
		}
		if(record == null) {
			throw "Could not find a record to convert this date";
		}

		// adjust the value by the UTC offset plus the DST save value
		var fakeVal = val + ((record.utco + record.s)/86400);
		// reset the value to round off the seconds
		fakeVal =
			altdate.JulianDay.convertToFloat(
				altdate.JulianDay.convertToTimeStruct(fakeVal)
			);
		var ts : TimeStruct = JulianDay.convertToTimeStruct(fakeVal);
		Reflect.setField(gdt,"year",ts.year);
		Reflect.setField(gdt,"month",ts.month);
		Reflect.setField(gdt,"day",ts.day);
		if(origAsTime) {
			Reflect.setField(gdt,"hours",ts.hours);
			Reflect.setField(gdt,"minutes",ts.minutes);
			Reflect.setField(gdt,"seconds",ts.seconds);
		}
		else {
			Reflect.setField(gdt,"hours",0);
			Reflect.setField(gdt,"minutes",0);
			Reflect.setField(gdt,"seconds",0);
		}
		Reflect.setField(gdt,"offset",record.utco);
		Reflect.setField(gdt,"save",record.s);
		Reflect.setField(gdt,"tzabbrev",_abbrev[record.i]);

		// reset the value to round off the seconds
		Reflect.setField(gdt,"value",
			altdate.JulianDay.convertToFloat(
				altdate.JulianDay.convertToTimeStruct(val)
			)
		);
	}


	/**
		Convert a Gregorian date or date/time that is in
		local time for this zone into UTC.
	*/
	public function remove(gdt : Gregorian) : Void
	{
		var ts : TimeStruct = JulianDay.convertToTimeStruct(
			gdt.value
		);
		Reflect.setField(gdt,"year",ts.year);
		Reflect.setField(gdt,"month",ts.month);
		Reflect.setField(gdt,"day",ts.day);
		if(gdt.isDateTime()) {
			Reflect.setField(gdt,"hours",ts.hours);
			Reflect.setField(gdt,"minutes",ts.minutes);
			Reflect.setField(gdt,"seconds",ts.seconds);
		}
		else {
			Reflect.setField(gdt,"hours",0);
			Reflect.setField(gdt,"minutes",0);
			Reflect.setField(gdt,"seconds",0);
		}

		Reflect.setField(gdt,"offset",0);
		Reflect.setField(gdt,"save",0);
		Reflect.setField(gdt,"tzabbrev","UTC");
		Reflect.setField(gdt,"timezone", new tz.zones.etc.UTC());
	}
}
